﻿using ImageFanReloaded.CommonTypes.CommonEventArgs;
using ImageFanReloaded.CommonTypes.Info;
using ImageFanReloaded.Factories;
using ImageFanReloaded.Views.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace ImageFanReloaded.Presenters
{
    public class MainPresenter
    {
        public MainPresenter(IMainView mainView)
        {
            if (mainView == null)
                throw new ArgumentNullException("mainView", "Main view cannot be null.");

            _mainView = mainView;
            _populateThumbnailsLockObject = new object();

            _mainView.FolderChanged += OnFolderChanged;

            PopulateDrivesAndSpecialFolders();
        }


        #region Private

        private IMainView _mainView;
        private object _populateThumbnailsLockObject;
        private CancellationTokenSource _cancellationTokenSource;

        private void OnFolderChanged(object sender, FileSystemEntryChangedEventArgs e)
        {
            StopPopulateThumbnails();

            PopulateSubFolders(e.Path);

            PopulateThumbnails(e.Path);
        }

        private void PopulateDrivesAndSpecialFolders()
        {
            var specialFolders = TypesFactoryResolver.TypesFactoryInstance
                                            .DiscQueryEngineInstance.GetSpecialFolders();
            _mainView.PopulateSubFoldersTree(specialFolders, true);
            
            var drives = TypesFactoryResolver.TypesFactoryInstance
                                            .DiscQueryEngineInstance.GetAllDrives();
            _mainView.PopulateSubFoldersTree(drives, true);
        }

        private void PopulateSubFolders(string folderPath)
        {
            var subFolders = TypesFactoryResolver.TypesFactoryInstance
                                            .DiscQueryEngineInstance.GetSubFolders(folderPath);
            _mainView.PopulateSubFoldersTree(subFolders, false);
        }

        private void StopPopulateThumbnails()
        {
            try
            {
                _cancellationTokenSource.Cancel();
            }
            catch
            {
            }
        }

        private async void PopulateThumbnails(string folderPath)
        {
            var entered = false;
            
            try
            {
                Monitor.Enter(_populateThumbnailsLockObject, ref entered);

                using (_cancellationTokenSource = new CancellationTokenSource())
                {
                    var token = _cancellationTokenSource.Token;

                    var getImageFilesTask = Task.Run<IEnumerable<ThumbnailInfo>>(() =>
                    {
                        var imageFiles = TypesFactoryResolver.TypesFactoryInstance
                                                        .DiscQueryEngineInstance
                                                        .GetImageFiles(folderPath);

                        var thumbnailInfoCollection =
                            imageFiles
                                .Select(anImageFile => new ThumbnailInfo(anImageFile))
                                .ToArray();

                        return thumbnailInfoCollection;
                    });

                    var thumbnails = await getImageFilesTask;

                    if (token.IsCancellationRequested)
                        return;

                    _mainView.ClearThumbnailBoxes();

                    for (var thumbnailCollection = thumbnails;
                             thumbnailCollection.Any();
                             thumbnailCollection =
                                        thumbnailCollection.Skip(GlobalData.ProcessorCount))
                    {
                        var currentThumbnails =
                                        thumbnailCollection.Take(GlobalData.ProcessorCount);

                        var readThumbnailInputTask = Task.Run<TaskStatus>(() =>
                        {
                            foreach (var aThumbnail in currentThumbnails)
                            {
                                if (token.IsCancellationRequested)
                                    return TaskStatus.Canceled;

                                aThumbnail.ImageFile.ReadThumbnailInputFromDisc();
                            }

                            if (token.IsCancellationRequested)
                                return TaskStatus.Canceled;
                            else
                                return TaskStatus.RanToCompletion;
                        }, _cancellationTokenSource.Token);

                        var readThumbnailInputTaskStatus = await readThumbnailInputTask;
                        if (readThumbnailInputTaskStatus == TaskStatus.Canceled)
                            return;
 
                        _mainView.PopulateThumbnailBoxes(currentThumbnails);

                        var getThumbnailsTask = Task.Run(() =>
                        {
                            currentThumbnails
                                .AsParallel()
                                .AsOrdered()
                                .ForAll(aThumbnail =>
                                {
                                    if (!token.IsCancellationRequested)
                                    {
                                        var currentThumbnail = aThumbnail.ImageFile.Thumbnail;
                                        currentThumbnail.Freeze();

                                        aThumbnail.ThumbnailImage = currentThumbnail;
                                    }
                                });
                        }, _cancellationTokenSource.Token);
                    }
                }
            }
            finally
            {
                if (entered)
                    Monitor.Exit(_populateThumbnailsLockObject);
            }
        }

        #endregion
    }
}
